﻿using System;
using System.Collections.Generic;
using System.Linq;
using Natural.Helpers;
using Natural.Model;

namespace Natural.Managers
{
    public class Runtime
    {
        private static object syncRoot = new Object();
        private static RuntimeInfo _info = null;
        public static RuntimeInfo Info
        {
            get
            {
                if (_info == null)
                {
                    lock (syncRoot)
                    {
                        if (_info == null)
                        {
                            _info = new RuntimeInfo();
                            Load(_info);
                        }
                    }
                }
                return _info;

            }
        }

        private static void Load(RuntimeInfo runtimeInfo)
        {
            runtimeInfo.Providers = new Dictionary<string, ProviderInfo>();
            runtimeInfo.Actions = new Dictionary<string, ActionInfo>();
            runtimeInfo.Entities = new Dictionary<string, EntityInfo>();

            foreach (string name in Keys(Implementation.Info.Providers))
            {
                 ProviderInfo info = new ProviderInfo();
                 info.Name = name;
                 info.Decorators = Implementations(Implementation.Info.Decorators,name);
                 info.Strategies = Implementations(Implementation.Info.Providers,name);
                 info.Defualt = info.Strategies.OrderBy(p => p.Priority).FirstOrDefault();
                 runtimeInfo.Providers.Add( info.Name.ToUpper(), info);
            }
            foreach (string name in Keys(Implementation.Info.Actions))
            {                
                ActionInfo info = new ActionInfo();
                info.Name = name;
                info.Decorators = Implementations(Implementation.Info.Decorators,name);
                info.Strategies = Implementations(Implementation.Info.Actions,name);
                info.Observers= Implementations(Implementation.Info.Observers, name);
                info.Defualt = info.Strategies.Count == 1 ? info.Strategies[0] : info.Strategies.Where(p => p.Name.ToUpper() == "DEFAULT").FirstOrDefault();
                runtimeInfo.Actions.Add(info.Name.ToUpper(), info);
            }
            foreach (Type entityType in Implementation.Info.Entities)
            {
                Attributes.Entity att = ReflectionHelper.Attribute<Attributes.Entity>(entityType);
                EntityInfo info = new EntityInfo();
                info.Name = entityType.Name;
                info.IsRoot = att.IsRoot;
                info.TypeName = TypeName(entityType);
                info.Actions = new Dictionary<string, ActionInfo>();
                foreach (string actionName in Keys(Implementation.Info.Actions,entityType))
                {       
                    string key = actionName.ToUpper();
                    ActionInfo actionInfo = new ActionInfo();
                    actionInfo.Name = actionName;                    
                    actionInfo.Decorators = Implementations(Implementation.Info.Decorators,key,entityType);
                    actionInfo.Strategies = Implementations(Implementation.Info.Actions,key,entityType);
                    actionInfo.Observers = Implementations(Implementation.Info.Observers, key, entityType);
                    actionInfo.Defualt = actionInfo.Strategies.Count == 1 ? actionInfo.Strategies[0] : actionInfo.Strategies.Where(p => p.Name.ToUpper() == "DEFAULT").FirstOrDefault();
                    info.Actions.Add(key, actionInfo);
                }
                runtimeInfo.Entities.Add(info.Name.ToUpper(), info);
            }

            #region Old
            ////Decorators by Action
            //foreach (string key in info.Actions.Keys)
            //{
            //    ActionInfo actionInfo = info.Actions[key];
            //    actionInfo.

            //    list.ForEach(p => actionInfo.Decorators.Add(DecorateInfo(p)));

            //    List<Type> list = new List<Type>();
            //    foreach (Type p in Implementation.Info.ActionDecorators)
            //    {
            //        Attributes.ActionDecorator att = GetAttribute<Attributes.ActionDecorator>(p);
            //        if (att.ApplyTo == null && att.Action.ToUpper() == key)
            //            list.Add(p);
            //    }
            //    list.ForEach(p => actionInfo.Decorators.Add(DecorateInfo(p)));
            //    if (list.Count > 0)
            //    {
            //        ActionInfo actionInfo = info.Actions[key];
            //        foreach (Type p in list)
            //        {
            //            actionInfo.Decorators.Add(DecorateInfo(p));
            //        }
            //    }
            //}
            //#endregion

            //#region Acciones por entidad
            //info.Entities = new Dictionary<string, EntityInfo>();
            //foreach (Type entityType in Implementation.Info.Entities)
            //{
            //    EntityInfo entityInfo = new EntityInfo();
            //    entityInfo.TypeName = string.Format("{0}, {1}", entityType.FullName, entityType.Assembly.FullName);
            //    entityInfo.Actions = new Dictionary<string, ActionInfo>();

            //    actionNames = Implementation.Info.Actions
            //                                .Where(p => ImplementAction(entityType, p))
            //                                .GroupBy(p => GetAttribute<Attributes.Action>(p).Name)
            //                                .Select(p => p.Key).ToList();
            //    //Agrega acciones por entidad
            //    foreach (string actionName in actionNames)
            //    {
            //        Type baseActionType = Implementation.Info.Actions
            //                                .Where(p => GetAttribute<Attributes.Action>(p).Name == actionName && GetAttribute<Attributes.Action>(p).ApplyTo != null)
            //                                .Select(p => p)
            //                                .OrderBy(p => ReflectionHelper.Implement(entityType, GetAttribute<Attributes.Action>(p).ApplyTo))
            //                                .Select(p => p)
            //                                .FirstOrDefault();

            //        Type actionType = baseActionType.IsGenericType ? baseActionType.MakeGenericType(entityType) : baseActionType;
            //        entityInfo.Actions.Add(actionName.ToUpper(), CreateActionInfo(actionType));
            //    }
            //    //agrega los decoradores por accion-entidad                
            //    foreach (string key in entityInfo.Actions.Keys)
            //    {
            //        List<Type> list = new List<Type>();
            //        foreach (Type p in Implementation.Info.ActionDecorators)
            //        {
            //            Attributes.ActionDecorator att = GetAttribute<Attributes.ActionDecorator>(p);
            //            if (att.Action.ToUpper() == key && ReflectionHelper.Implement(entityType, att.ApplyTo) > 0)
            //                list.Add(p);
            //        }
            //        if (list.Count > 0)
            //        {
            //            ActionInfo actionInfo = entityInfo.Actions[key];
            //            actionInfo.Decorators = CreateDecorators(list, entityType);
            //        }
            //    }
            //    //agregar el entityInfo
            //    info.Entities.Add(entityType.Name.ToUpper(), entityInfo);
            //}
            //#endregion
            #endregion

        }
        private static List<string> Keys(List<Type> source)
        {
            if (source == null || source.Count == 0) return new List<string>();
            return source.Select(p => ReflectionHelper.Attribute<Attributes.Impl>(p)).Where(p => p.ApplyTo == null).Select(p => p.Name).Distinct().ToList();
        }
        private static List<string> Keys(List<Type> source, Type applyTo)
        {
            if (source == null || source.Count == 0) return new List<string>();
            return source.Select(p => ReflectionHelper.Attribute<Attributes.Impl>(p)).Where(p => p.ApplyTo!=null && ReflectionHelper.Implement(applyTo, p.ApplyTo) > 0).Select(p => p.Name).Distinct().ToList();
        }
        private static List<ImplementInfo> Implementations(List<Type> source, string name)
        {
            if (source == null || source.Count == 0) return new List<ImplementInfo>();
            List<ImplementInfo> target = new List<ImplementInfo>();
            foreach (var p in source
                            .Select(p => new { Type = p, Att = ReflectionHelper.Attribute<Attributes.Impl>(p) })
                            .Where(p => p.Att.Name == name && p.Att.ApplyTo==null)
                            .GroupBy(p => p.Att.Strategy)
                            .Select(g => new { Strategy = g.Key, Info = g.OrderByDescending(p => p.Att.Priority).ThenByDescending(p=>p.Att.Version).Select(p => new { Type = p.Type, Att = p.Att }).FirstOrDefault() })
                            .ToList())
            {
                ImplementInfo info = new ImplementInfo();
                info.TypeName = TypeName(p.Info.Type);
                info.Priority = p.Info.Att.Priority;
                info.Name = p.Strategy;
                target.Add(info);
            }
            return target;
        }
        private static List<ImplementInfo> Implementations(List<Type> source, string name, Type applyTo)
        {
            if (source == null || source.Count == 0) return new List<ImplementInfo>();
            List<ImplementInfo> target = new List<ImplementInfo>();
            foreach (var p in source
                            .Select(p => new { Type = p, Att = ReflectionHelper.Attribute<Attributes.Impl>(p) })
                            .Where(p => p.Att.Name.ToUpper() == name.ToUpper() && ReflectionHelper.Implement(applyTo,p.Att.ApplyTo)>0 )
                            .GroupBy(p => p.Att.Strategy)
                            .Select(g => new { Strategy = g.Key, Info = g.OrderByDescending(p => p.Att.Version).Select(p => new { Type = p.Type, Att = p.Att }).FirstOrDefault() })
                            .ToList())
            {
                Type type = p.Info.Type.IsGenericType ? p.Info.Type.MakeGenericType(applyTo) : p.Info.Type;

                ImplementInfo info = new ImplementInfo();
                info.TypeName = TypeName(type);
                info.Priority = p.Info.Att.Priority;
                info.Name = p.Strategy;
                target.Add(info);
            }
            return target;
        }
        private static string TypeName(Type type)
        {
            return string.Format("{0}, {1}", type.FullName, type.Assembly.FullName);
        }
    }
    

}
